﻿using System;
using System.Text;
using RoMBot.MemoryLib;

namespace RoMBot
{
    public static class Macros
    {
/*
        private static int _commandMacro = 0;
        private static int _resultMacro = 0;
*/

        public const string CommandMacroName = "RB Command";
        public const string ResultMacroName = "RB";

        public static void SetupMacros()
        {
            // See if there is old version macros
            //var macro = ReadMacro( 1 );
            //if( settings.options.LANGUAGE == "spanish" ) then
            //    scriptDef = "/redactar";
            //else
            //    scriptDef = "/script";
            //end

            // Find commandMacro
            Bot.Instancia.CommandMacro = ( int ) FindMacroByName( CommandMacroName );
            if ( Bot.Instancia.CommandMacro == 0 )
            {
                Bot.Instancia.CommandMacro = ( int ) FindEmptyMacro();
                if ( Bot.Instancia.CommandMacro > 0 )
                {
                    WriteToMacro( Bot.Instancia.CommandMacro, null, CommandMacroName, 1 );
                }
                else
                {
                    throw new Exception( "No quedan macros vacías." );
                }
            }
            // Find resultMacro
            Bot.Instancia.ResultMacro = ( int ) FindMacroByName( ResultMacroName );
            if ( Bot.Instancia.ResultMacro == 0 || Bot.Instancia.ResultMacro == Bot.Instancia.CommandMacro )
            {
                Bot.Instancia.ResultMacro = ( int ) FindEmptyMacro();
                if ( Bot.Instancia.ResultMacro > 0 )
                {
                    WriteToMacro( Bot.Instancia.ResultMacro, null, ResultMacroName, 1 );
                }
                else
                {
                    throw new Exception( "No quedan macros vacías." );
                }
            }

            //if(settings.options.DEBUGGING_MACRO) then
            //    printf("commandMacro set to " .. commandMacro .. ".\n")
            //    printf("resultMacro set to " .. resultMacro .. ".\n")
            //end

            SetupMacroHotkey();
            //setupAttackKey()
        }

        public static void SetupMacroHotkey()
        {
            // Set settings.profile.hotkeys.MACRO.key
            var hotkey = GetHotkeyByName( "TOGGLEPLATES" );// -- The "Toggle title/guild" hotkey
            if ( hotkey.Key == Key.VK_NONE || hotkey.Modifier != Key.VK_NONE )
            {
                throw new Exception( "Please assign a hotkey to 'Show title/guild' in the games 'Key Bindings' interface. Dont use a modifier(CTRL,SHIFT,ALT)." );
            }

            Profile.HotKeys.Macro = hotkey;
            //settings.profile.hotkeys.MACRO.key = hotkey

            //if( settings.options.DEBUGGING_MACRO ) then
            //    printf("The macro hotkey is ".. string.char(hotkey) .. ".\n")
            //end
        }

        public static void SetupAttackKey()
        {
            //Profile.HotKeys.AttackType = HotKey.Empty;

            Profile.HotKeys.AttackType = new HotKey { Key = Key.MACRO, Modifier = Key.VK_NONE, Name = "Attack" };
        }

        public static void WriteToMacro( int macroNum, string body = null, string name = null, int icon = 0 )
        {
            // Check macroNum
            if ( macroNum < 1 || macroNum > 49 )
            {
                throw new ArgumentException( "Macro number needs to be between 1 and 49." );
            }

            // Get macros base address
            var macroAddress = Memory.ReadUInt( Addresses.staticbase_macro );

            // Write the macro body
            if ( !string.IsNullOrEmpty( body ) )
            {
                var bytes = Encoding.UTF8.GetBytes( body );
                var guardar = new byte[ bytes.Length + 1 ];
                bytes.CopyTo( guardar, 0 );
                //guardar = 
                guardar[ bytes.Length ] = 0;
                Memory.WriteBytes( ( int ) macroAddress + Addresses.macroSize * ( macroNum - 1 ) + Addresses.macroBody_offset, guardar, guardar.Length );
            }

            // Write the macro name
            if ( !string.IsNullOrEmpty( name ) )
            {
                for ( var i = 0; i <= name.Length; i++ )
                {
                    var _byte = ( i < name.Length ? Convert.ToByte( name[ i ] ) : ( byte ) 0 );
                    if ( _byte == 0 )
                    {
                        Memory.WriteByte( macroAddress + Addresses.macroSize * ( uint ) ( macroNum - 1 ) + Addresses.macroName_offset + ( uint ) i, 0 );
                        break;
                    }
                    Memory.WriteByte( macroAddress + Addresses.macroSize * ( uint ) ( macroNum - 1 ) + Addresses.macroName_offset + ( uint ) i, _byte );
                }
            }

            // Set the macro icon
            if ( icon > 0 && icon <= 60 )
            {
                Memory.WriteInt( ( uint ) ( macroAddress + Addresses.macroSize * ( macroNum - 1 ) + Addresses.macroIcon_offset ), icon );
            }
        }

        public static Macro ReadMacro( int macroNum )
        {
            if ( macroNum < 1 || macroNum > 49 )
            {
                throw new Exception( "The macro number must be a number between 1 and 49." );
            }

            // Get macros base address
            var macroAddress = Memory.ReadUInt( Addresses.staticbase_macro );

            // Read the macro body
            var body = Memory.ReadUtf8String( ( int ) macroAddress + Addresses.macroSize * ( macroNum - 1 ) + Addresses.macroBody_offset );
            var name = "";
            for ( var i = 0; i < 31; i++ )
            {
                var _byte = Memory.ReadUByte( macroAddress + Addresses.macroSize * ( macroNum - 1 ) + Addresses.macroName_offset + i );

                if ( _byte == 0 )// Break on NULL terminator
                {
                    break;
                }
                name = name + Convert.ToChar( _byte );
            }

            // Read the macro icon
            var icon = Memory.ReadUInt( ( uint ) ( macroAddress + Addresses.macroSize * ( macroNum - 1 ) + Addresses.macroIcon_offset ) );

            // Read the macro id
            var id = Memory.ReadUInt( ( uint ) ( macroAddress + Addresses.macroSize * ( macroNum - 1 ) + Addresses.macroId_offset ) );

            // return Macro
            return new Macro { Body = body, Name = name, Icon = icon, Id = id };
        }

        public static uint SearchForMacro( string bodypattern = null, string namematch = null, uint iconmatch = 0, uint idmatch = 0 )
        {
            if ( string.IsNullOrEmpty( bodypattern ) && string.IsNullOrEmpty( namematch ) && iconmatch <= 0 && idmatch <= 0 )
            {
                throw new ArgumentException( "You need to include at least 1 argument to the 'SearchForMacro()' function." );
            }

            for ( uint i = 1; i < 50; i++ )
            {
                var macro = ReadMacro( ( int ) i );
                if (
                    ( namematch == null || namematch == macro.Name ) &&
                    ( iconmatch <= 0 || iconmatch == macro.Icon ) &&
                    ( idmatch <= 0 || idmatch == macro.Id ) &&
                    ( bodypattern == null || macro.Body.Contains( bodypattern ) )
                   )
                {
                    return i;
                }
            }
            return 0;
        }

        public static uint FindMacroByName( string macroName )
        {
            return SearchForMacro( null, macroName );
        }

        public static uint FindEmptyMacro()
        {
            return SearchForMacro( "", "", 0xFFFFFFFF );
        }

        public static HotKey GetHotkey( int number )
        {
            var hotkeysTableAddress = Memory.ReadUIntPtr( Addresses.hotkeysPtr, Addresses.hotkeys_offset );
            var hotkeyAddress = Memory.ReadUInt( hotkeysTableAddress + ( uint ) ( 0x4 * ( number - 1 ) ) );
            if ( hotkeyAddress < 1 )// invalid number
            {
                return HotKey.Empty;
            }
            var hotkey = Memory.ReadUByte( hotkeyAddress + Addresses.hotkeysKey_offset );

            var name = Memory.ReadString( hotkeyAddress + Addresses.hotkeysName_offset );
            //TODO: This regex is wrong
            if ( string.IsNullOrEmpty( name ) || !System.Text.RegularExpressions.Regex.IsMatch( name, "[%u%d_]*" ) )
            {
                name = Memory.ReadStringPtr( ( int ) hotkeyAddress + Addresses.hotkeysName_offset, 0 );
            }
            var tempModifier = Memory.ReadUByte( hotkeyAddress + Addresses.hotkeysKey_offset + 2 );
            var modifier = Key.VK_NONE;
            switch ( tempModifier )
            {
                case 1:
                    modifier = Key.VK_SHIFT;
                    break;
                case 2:
                    modifier = Key.VK_CONTROL;
                    break;
                case 4:
                    modifier = Key.VK_ALT;
                    break;
            }

            return new HotKey { Key = ( Key ) hotkey, Modifier = modifier, Name = name };
        }

        public static HotKey GetHotkeyByName( string name )
        {
            var number = 0;
            HotKey hotkey;
            do
            {
                number++;
                hotkey = GetHotkey( number );
                if ( string.IsNullOrEmpty( hotkey.Name ) )
                {
                    return HotKey.Empty;
                }
            } while ( hotkey.Name != name );

            return hotkey;
        }
    }

    public class Macro
    {
        public uint Id { get; set; }
        public string Name { get; set; }
        public string Body { get; set; }
        public uint Icon { get; set; }
    }
}
